﻿// -----------------------------------------------------------------------
// <copyright file="AcknowledgementService.cs" company="Zen Design Corp">
// TODO: Update copyright text.
// </copyright>
// -----------------------------------------------------------------------

namespace Zen.BlackBerry.Acknowledgement
{
	using System;
	using System.Collections.Generic;
	using System.Linq;
	using System.Threading.Tasks;
	using Microsoft.Practices.Unity;
	using Zen.BlackBerry.Configuration;
	using Zen.BlackBerry.Pap;
	using Zen.BlackBerry.Pap.Control;
	using Zen.BlackBerry.Pap.Parser;
	using Zen.BlackBerry.Push;
	using Zen.BlackBerry.Push.AppManagement;
	using Zen.BlackBerry.Push.Count;
	using Zen.BlackBerry.Push.Request;

	/// <summary>
	/// Implementation of the <see cref="T:IAcknowledgementService"/> interface.
	/// </summary>
	public class AcknowledgementService : IAcknowledgementService
	{
		private IPushProperties _pushProperties;
		private IPapService _papService;
		private IPushApplicationService _appService;
		private IPushCountService _countService;
		private IPushRequestDetailService _requestDetailService;
		private INotificationProcessorService _processorService;

		/// <summary>
		/// Initializes a new instance of the <see cref="AcknowledgementService"/> class.
		/// </summary>
		/// <param name="pushProperties">The push properties.</param>
		/// <param name="papService">The pap service.</param>
		/// <param name="appService">The app service.</param>
		/// <param name="countService">The count service.</param>
		/// <param name="requestDetailService">The request detail service.</param>
		/// <param name="processorService">The processor service.</param>
		public AcknowledgementService(
			[Dependency]IPushProperties pushProperties,
			[Dependency]IPapService papService,
			[Dependency]IPushApplicationService appService,
			[Dependency]IPushCountService countService,
			[Dependency]IPushRequestDetailService requestDetailService,
			[Dependency]INotificationProcessorService processorService)
		{
			_pushProperties = pushProperties;
			_papService = papService;
			_appService = appService;
			_countService = countService;
			_requestDetailService = requestDetailService;
			_processorService = processorService;
		}

		private async void SendNotifications(IEnumerable<PushIdAndApplicationId> pushIdAndApplicationIds)
		{
			foreach (PushIdAndApplicationId pushIdAndApplicationId in pushIdAndApplicationIds)
			{
				int? addressCount = null;
				try
				{
					PushApplication pushApplication = _appService.FindByAppId(pushIdAndApplicationId.ApplicationId);

					StatusQueryResponse response = await PerformStatusQuery(pushIdAndApplicationId, pushApplication);

					addressCount = null;
					if ((pushApplication.IsLastNotificationEnabled) ||
						(pushApplication.AutoDeleteRequests))
					{
						addressCount = response.Results.Sum((item) => item.Addresses.Count);

						if (addressCount > 1)
						{
							_countService.Remove(pushIdAndApplicationId.PushId);
							_countService.Add(pushIdAndApplicationId.PushId);
						}
					}

					foreach (var statusResult in response.Results.Where((item) =>
						item.MessageState != MessageState.Pending &&
						item.MessageState != MessageState.Unknown))
					{
						if (statusResult.Addresses.Count == 0)
						{
							SendNotification(pushIdAndApplicationId, statusResult, "*", addressCount);
						}
						else
						{
							foreach (string address in statusResult.Addresses)
							{
								SendNotification(pushIdAndApplicationId, statusResult, address, addressCount);
							}
						}
					}
				}
				catch (Exception)
				{
					//log.error("Error requesting notifications for " + pushIdAndApplicationIds, e);
				}
			}
		}

		private void SendNotification(
			PushIdAndApplicationId pushIdAndApplicationId,
			StatusQueryResult statusResult,
			string address,
			int? addressCount)
		{
			Notification notification = new Notification();
			notification.Address = address;
			notification.Code = statusResult.Code;
			notification.Description = statusResult.Description;
			notification.EventTime = statusResult.EventTime;
			notification.MessageState = statusResult.MessageState;
			notification.QualityOfService = statusResult.QualityOfService;
			notification.PushId = pushIdAndApplicationId.PushId;
			notification.PushApplicationId = pushIdAndApplicationId.ApplicationId;
			notification.AddressCount = addressCount;

			//if (log.isDebugEnabled())
			//{
			//    log.debug("notification=" + notification);
			//}

			_processorService.AddNotification(notification);
		}

		private async Task<StatusQueryResponse> PerformStatusQuery(
			PushIdAndApplicationId pushIdAndApplicationId,
			PushApplication pushApplication)
		{
			StatusQueryResponse response = null;
			if (pushApplication.Type == PushApplicationType.EnterprisePush)
			{
				StatusQueryMessageControl statusQueryMessage =
					new StatusQueryMessageControl(false);
				statusQueryMessage.PushId = pushIdAndApplicationId.PushId;
				statusQueryMessage.PushProperties = _pushProperties;

				response = await _papService.StatusQuery(
					pushApplication.UserName,
					pushApplication.EnterprisePassword,
					statusQueryMessage);
			}
			else if (pushApplication.Type == PushApplicationType.PublicPush)
			{
				StatusQueryMessageControl statusQueryMessage =
					new StatusQueryMessageControl(true);
				statusQueryMessage.PushId = pushIdAndApplicationId.PushId;
				statusQueryMessage.PushProperties = _pushProperties;

				response = await _papService.StatusQuery(
					pushApplication.RimApplicationId,
					pushApplication.PublicPassword,
					statusQueryMessage);
			}
			else
			{
				StatusQueryMessageControl statusQueryMessage = new StatusQueryMessageControl(false);
				statusQueryMessage.PushId = pushIdAndApplicationId.PushId;
				statusQueryMessage.PushProperties = _pushProperties;

				response = await _papService.StatusQuery(
					pushApplication.UserName,
					pushApplication.EnterprisePassword,
					statusQueryMessage);

				statusQueryMessage = new StatusQueryMessageControl(true);
				statusQueryMessage.PushId = pushIdAndApplicationId.PushId;
				statusQueryMessage.PushProperties = _pushProperties;

				StatusQueryResponse statusQueryResponseBIS =
					await _papService.StatusQuery(
						pushApplication.RimApplicationId,
						pushApplication.PublicPassword,
						statusQueryMessage);

				response.Results.AddRange(statusQueryResponseBIS.Results);
			}
			return response;
		}

		#region IAcknowledgementService Members

		/// <summary>
		/// Requests notification for the given push ids. Used in the case where
		/// result notifications may have been lost.
		/// </summary>
		/// <param name="pushIds">The push ids to request notifications for.</param>
		/// <returns>
		/// An enumerable collection of invalid push-ids for which there are
		/// no pending requests.
		/// </returns>
		public IEnumerable<string> RequestNotification(IEnumerable<string> pushIds)
		{
			//if (log.isDebugEnabled())
			//{
			//    log.debug("AcknowledgementServiceImpl.requestNotification: pushIds=" + pushIds);
			//}

			//if (this.notificationProcessorService.isResultNotificationQueueFull()) {
			//  throw new PushSDKException(new FullNotificationQueueException());
			//}

			// Get information on messages in the pending state using push-ids
			//	if specified.
			var pushIdAndApplicationIds =
				_requestDetailService.FindByIdsAndState(
					pushIds, MessageState.Pending);

			// Determine the push ids which we regard as being invalid
			List<string> invalidPushIds = new List<string>();
			if (pushIds != null)
			{
				// All ids given by the caller are considered invalid
				invalidPushIds.AddRange(pushIds);

				// Excluding any for which we have pending messages...
				foreach (PushIdAndApplicationId pushIdAndApplicationId in pushIdAndApplicationIds)
				{
					invalidPushIds.Remove(pushIdAndApplicationId.PushId);
				}
			}

			// Now we will get the status of all pending messages...
			TaskEx.Run(() => SendNotifications(pushIdAndApplicationIds));
			return invalidPushIds;
		}

		#endregion
	}
}
